package psd.model.Prolog;

import java.util.Vector;

import org.eclipse.jdt.core.dom.ArrayType;
import org.eclipse.jdt.core.dom.BooleanLiteral;
import org.eclipse.jdt.core.dom.CharacterLiteral;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.NullLiteral;
import org.eclipse.jdt.core.dom.NumberLiteral;
import org.eclipse.jdt.core.dom.ParameterizedType;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeLiteral;

public class TypeResolver implements ITypeResolver {

	private IFQNTranslator fqnresolve;
	private IIDResolver idResolver;

	public TypeResolver(IFQNTranslator fqnresolve, IIDResolver idresolver) {
		this.fqnresolve = fqnresolve;
		idResolver = idresolver;
	}

	public String getTypeTerm(Type nodetype) {
		return getTypeTerm(nodetype, 0);
	}

	public String getTypeTerm(Type type, int extradim) {
		if (type == null) {
			throw new IllegalArgumentException("null passed to TypeResolver");
		}
		int dim = extradim;
		if (type.isArrayType()) {
			ArrayType at = (ArrayType) type;
			dim += at.getDimensions();
			type = at.getElementType();
		}
		if (type.isPrimitiveType()) {
			PrimitiveType pt = (PrimitiveType) type;
			return (new StringBuilder("type(basic, ")).append(
					pt.getPrimitiveTypeCode()).append(", ").append(dim).append(
					")").toString();
		}
		if (type instanceof ParameterizedType) {
			throw new IllegalArgumentException(
					"Java 5 Generics are not supported, yet!");
		}
		SimpleType st = (SimpleType) type;
		Name name = st.getName();
		ITypeBinding bind = (ITypeBinding) name.resolveBinding();
		if (name.isQualifiedName()) {
			return getClassTypeTerm(getFullyQualifiedName(bind), dim);
		}
		if (bind == null) {
			throw new IllegalArgumentException("Type name can not be qualified");
		} else {
			return getClassTypeTerm(getFullyQualifiedName(bind), dim);
		}
	}

	private String getFullyQualifiedName(ITypeBinding bind) {
		String fqn = bind.getErasure().getQualifiedName();
		return IDResolver.normalizeFullQualifiedName(fqn);
	}

	protected String getClassTypeTerm(String fqn, int dim) {
		String fqnTerm = (new StringBuilder("fqn('")).append(fqn).append("')")
				.toString();
		String rv = (new StringBuilder("type(class, ")).append(
				fqnresolve.transformFQN(fqnTerm)).append(", ").append(dim)
				.append(")").toString();
		return rv;
	}

	private String getClassTypeTerm(ITypeBinding type, int dim) {
		String rv = (new StringBuilder("type(class, ")).append(
				idResolver.getID(type)).append(", ").append(dim).append(")")
				.toString();
		return rv;
	}

	public String getTypeTerm(StringLiteral m) {
		if (m == null) {
			throw new IllegalArgumentException("null passed to TypeResolver");
		} else {
			return getClassTypeTerm("java.lang.String", 0);
		}
	}

	public String getTypeTerm(CharacterLiteral c) {
		if (c == null) {
			throw new IllegalArgumentException("null passed to TypeResolver");
		} else {
			return "type(basic, char, 0)";
		}
	}

	public String getTypeTerm(NullLiteral n) {
		if (n == null) {
			throw new IllegalArgumentException("null passed to TypeResolver");
		} else {
			return "type(basic, null, 0)";
		}
	}

	public String getTypeTerm(BooleanLiteral b) {
		if (b == null) {
			throw new IllegalArgumentException("null passed to TypeResolver");
		} else {
			return "type(basic, boolean, 0)";
		}
	}

	public String getTypeTerm(NumberLiteral n)
{
    String token;
    if(n == null)
    {
        throw new IllegalArgumentException("null passed to getTypeTerm");
    }
    token = n.getToken();
    if(token.endsWith("l") || token.endsWith("L"))
    {
        return "type(basic, long, 0)";
    }
    if(token.endsWith("d") || token.endsWith("D"))
    {
        return "type(basic, double, 0)";
    }
    if(token.endsWith("f") || token.endsWith("F"))
    {
        return "type(basic, float, 0)";
    }
    try{		
    long lvalue = Long.parseLong(token);
    if(lvalue > 0x7fffffffL || lvalue < 0xffffffff80000000L)
    {
        return "type(basic, long, 0)";
    } else
    {
        return "type(basic, int, 0)";
    }
    }
    catch(Exception e)
    {
    	double dvalue = Double.parseDouble(token);
    	 return "type(basic, double, 0)";    	
    }
}

	public String getTypeTerm(TypeLiteral n) {
		if (n == null) {
			throw new IllegalArgumentException("null passed to TypeResolver");
		} else {
			return getClassTypeTerm("java.lang.Class", 0);
		}
	}

	public boolean isTypeTerm(String str) {
		if (str == null || str.length() == 0) {
			return false;
		}
		char chars[] = str.toCharArray();
		if (chars[chars.length - 1] != ')') {
			return false;
		}
		if (!str.startsWith("type(")) {
			return false;
		}
		StringBuffer type = new StringBuffer();
		StringBuffer name = new StringBuffer();
		StringBuffer num = new StringBuffer();
		StringBuffer sbs[] = { type, name, num };
		int index = 0;
		for (int i = 5; i < chars.length; i++) {
			char c = chars[i];
			if (c == ',') {
				i++;
				index++;
				if (chars[i] != ' ' || index >= 3) {
					return false;
				}
			} else {
				sbs[index].append(c);
			}
		}

		if (index < 2) {
			return false;
		}
		String fqn = name.toString();
		String foo = num.toString();
		if (!foo.endsWith(")")) {
			return false;
		}
		foo = foo.substring(0, foo.length() - 1);
		int number;
		try {
			number = Integer.parseInt(foo);
		} catch (NumberFormatException _ex) {
			return false;
		}
		if (type.toString().equals("basic")) {
			if (fqn.equals("int") || fqn.equals("long") || fqn.equals("double")
					|| fqn.equals("float")) {
				return number >= 0;
			}
			if (fqn.equals("void") || fqn.equals("null")) {
				return number == 0;
			}
		} else if (type.toString().equals("class")) {
			if (number < 0) {
				return false;
			}
			if (!fqn.startsWith("fqn(")) {
				return false;
			}
			fqn = fqn.substring(4);
			char split[] = fqn.toCharArray();
			if (split[0] != '\'') {
				return false;
			}
			if (!Character.isJavaIdentifierStart(split[1])) {
				return false;
			}
			for (int i = 2; i < split.length - 2; i++) {
				char c = split[i];
				if (!Character.isJavaIdentifierPart(c) && c != '.') {
					return false;
				}
			}

			if (split[split.length - 2] != '\'') {
				return false;
			}
			if (split[split.length - 1] != ')') {
				return false;
			} else {
				return notReservedWord(fqn.substring(1, fqn.length() - 1));
			}
		}
		return false;
	}

	private boolean notReservedWord(String type) {
		Vector v = new Vector();
		v.add("abstract");
		v.add("boolean");
		v.add("break");
		v.add("byte");
		v.add("case");
		v.add("catch");
		v.add("char");
		v.add("class");
		v.add("const");
		v.add("continue");
		v.add("default");
		v.add("do");
		v.add("double");
		v.add("else");
		v.add("extends");
		v.add("final");
		v.add("finally");
		v.add("float");
		v.add("for");
		v.add("goto");
		v.add("if");
		v.add("implements");
		v.add("import");
		v.add("instanceof");
		v.add("int");
		v.add("interface");
		v.add("long");
		v.add("native");
		v.add("new");
		v.add("null");
		v.add("package");
		v.add("private");
		v.add("protected");
		v.add("public");
		v.add("return");
		v.add("short");
		v.add("static");
		v.add("strictfp");
		v.add("super");
		v.add("switch");
		v.add("synchronized");
		v.add("this");
		v.add("throw");
		v.add("throws");
		v.add("transient");
		v.add("try");
		v.add("void");
		v.add("volatile");
		v.add("while");
		return !v.contains(type);
	}

	public String getTypeTerm(ITypeBinding type) {
		if (type == null) {
			return getClassTypeTerm(type, 0);
		}
		int dim = 0;
		if (type.isArray()) {
			dim += type.getDimensions();
			type = type.getElementType();
		}
		if (type.isPrimitive()) {
			return (new StringBuilder("type(basic, ")).append(type.getName())
					.append(", ").append(dim).append(")").toString();
		} else {
			return getClassTypeTerm(type, dim);
		}
	}
}
